package com.feiyun.common.ftp;

import com.feiyun.common.pool.SftpGenericObjectPool;
import com.jcraft.jsch.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;


public class SftpUtil {
    //连接池
    private SftpGenericObjectPool pool;

    @Value("${sftp.host}")
    private String host;
    @Value("${sftp.port}")
    private int port;
    @Value("${sftp.username}")
    private String username;
    @Value("${sftp.password}")
    private String password;


    public SftpUtil(SftpGenericObjectPool pool) {
        this.pool = pool;
    }

    public boolean isExist(String filePath) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            sftp.lstat(filePath);
        } catch (SftpException se) {
            if (se.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                return false;
            }
        } finally {
            pool.returnObject(sftp);
        }
        return true;
    }



    /**
     * 下载文件
     *
     * @param dir  远程目录
     * @param name 远程文件名
     * @return 文件字节数组
     */
    public InputStream download(String dir, String name) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            sftp.cd(dir);
            return sftp.get(name);
        } finally {
            pool.returnObject(sftp);
        }
    }

    /**
     * 根据全路径下载文件
     * @param filePath 远程文件全路径
     * @return 文件流
     * @throws Exception
     */
    public InputStream download(String filePath) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            return sftp.get(filePath);
        } finally {
            pool.returnObject(sftp);
        }
    }

    /**
     * 通过http请求下载文件
     * @param response HttpServletResponse
     * @param filePath 文件全路径
     * @param realFileName 下载出来的文件名
     * @throws Exception
     */
    public void download(HttpServletResponse response, String filePath, String realFileName) throws Exception {
        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(realFileName,"UTF-8"));
        ServletOutputStream os = response.getOutputStream();
        BufferedInputStream bis = new BufferedInputStream(download(filePath));
        byte[] buff = new byte[1024];
        int i = 0;
        while ((i = bis.read(buff)) != -1) {
            os.write(buff, 0, i);
            os.flush();
        }
        bis.close();
        os.close();
    }

    /**
     * 拿到了File，下载到浏览器
     * @param response HttpServletResponse
     * @param file 文件对象
     * @param realFileName 下载的文件名
     * @throws Exception
     */
    public void download(HttpServletResponse response, File file, String realFileName) throws Exception {
        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setContentLength((int) file.length());
        response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(realFileName == null ? file.getName() : realFileName,"UTF-8"));
        ServletOutputStream os = response.getOutputStream();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        byte[] buff = new byte[1024];
        int i = 0;
        while ((i = bis.read(buff)) != -1) {
            os.write(buff, 0, i);
            os.flush();
        }
        bis.close();
        os.close();
    }

    /**
     * 下载远程文件并读取每一行数据
     * @param filePath 远程文件全路径
     * @return 文件内容
     */
    public List<String> downloadAndReadAllLines(String filePath) {
        ChannelSftp sftp = null;
        try {
            sftp = pool.borrowObject();
            InputStream inputStream = sftp.get(filePath);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            ArrayList<String> result = new ArrayList<>();
            for (; ; ) {
                String line = bufferedReader.readLine();
                if (line == null) {
                    break;
                }
                result.add(line);
            }
            return result;
        } catch (Exception e) {
            return new ArrayList<>();
        } finally {
            if (sftp != null) {
                pool.returnObject(sftp);
            }
        }
    }

    /**
     * 上传文件
     *
     * @param dir  远程目录
     * @param name 远程文件名
     * @param in   输入流
     */
    public void upload(String dir, String name, InputStream in) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            mkdirs(sftp, dir);
            sftp.cd(dir);
            sftp.put(in, name);
        } finally {
            pool.returnObject(sftp);
        }
    }

    /**
     * 上传文件
     * @param filePath 远程文件全路径
     * @param in 文件流
     * @throws Exception
     */
    public void upload(String filePath, InputStream in) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            mkdirs(sftp, filePath);
            sftp.put(in, filePath);
        } finally {
            pool.returnObject(sftp);
        }
    }

    /**
     * 直接上传内容数据
     * @param filePath 远程文件全路径
     * @param data 数据内容
     * @throws Exception
     */
    public void upload(String filePath, String data) throws Exception {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
        this.upload(filePath, byteArrayInputStream);
    }

    /**
     * 删除文件
     *
     * @param dir  远程目录
     * @param name 远程文件名
     */
    public void delete(String dir, String name) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            sftp.cd(dir);
            sftp.rm(name);
        } finally {
            pool.returnObject(sftp);
        }
    }

    public void delete(String filePath) throws Exception {
        ChannelSftp sftp = pool.borrowObject();
        try {
            sftp.rm(filePath);
        } finally {
            pool.returnObject(sftp);
        }
    }

    /**
     * 递归创建多级目录
     *
     * @param dir 多级目录
     */
    private void mkdirs(ChannelSftp sftp, String dir) throws SftpException {
        String[] folders = dir.split("/");
        sftp.cd("/");
        for (String folder : folders) {
            if (folder.length() > 0) {
                try {
                    sftp.cd(folder);
                } catch (Exception e) {
                    sftp.mkdir(folder);
                    sftp.cd(folder);
                }
            }
        }
    }

    /**
     * 执行shell命令
     * @param command shell命令
     * 注意：执行shell命令不能使用pool来实现，否则只会第一次成功，后面执行会不起作用（也有可能是我代码的问题）
     */
    public void execShell(String command) {
        if (StringUtils.isEmpty(command)) {
            return;
        }
        command = command + " \n";

        JSch jsch = new JSch();
        Session sshSession = null;
        ChannelExec sftp = null;
        try {
            jsch.getSession(username,host,port);
            sshSession.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            sftp = (ChannelExec) sshSession.openChannel("exec");
            sftp.setCommand(command);
            OutputStream outputStream = sftp.getOutputStream();
            sftp.connect();

            outputStream.write(command.getBytes());
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sftp != null) {
                sftp.disconnect();
            }
            if (sshSession != null) {
                sshSession.disconnect();
            }
        }
    }

    public void copy(String srcPath, String dstPath) {
        String command = "cp " + srcPath + " " + dstPath + ";";
        this.execShell(command);
    }

    public SftpGenericObjectPool getPool() {
        return pool;
    }

    public void setPool(SftpGenericObjectPool pool) {
        this.pool = pool;
    }
}